﻿
namespace SS.Ynote.Engine
{
    /// <summary>
    /// Merger for three source files
    /// </summary>
    public static class Merger
    {
        /// <summary>
        /// Merge three files
        /// </summary>
        public static Lines Merge(Lines source, Lines first, Lines second)
        {
            Lines source2 = source.Clone();

            source.Merge(first);
            source2.Merge(second);

            Lines result = Join(source, source2);

            return result;
        }

        /// <summary>
        /// Compares and joins two Lines
        /// </summary>
        public static Lines Join(Lines source1, Lines source2)
        {
            Lines acc1 = source1[-1].subLines ?? new Lines();
            Lines acc2 = source2[-1].subLines ?? new Lines();
            Lines result = new Lines();

            FlushSubLines(source1[-1].subLines, source2[-1].subLines, result);

            for (int i = 0; i < source1.Count; i++)
            {
                if (source1[i].state == DiffType.Deleted || source2[i].state == DiffType.Deleted)
                {
                    ProcessConflictArea(ref i, source1, source2, result);
                    continue;
                }

                result.Add(source1[i]);

                FlushSubLines(source1[i].subLines, source2[i].subLines, result);
            }

            return result;
        }

        private static void ProcessConflictArea(ref int i, Lines source1, Lines source2, Lines result)
        {
            bool isConflict = source1[i].state == DiffType.Deleted && source2[i].state == DiffType.Deleted;
            int iStart = i;
            for (i=i+1; i < source1.Count; i++)
            {
                isConflict |= source1[i].state == DiffType.Deleted && source2[i].state == DiffType.Deleted;

                if (source2[i].state == DiffType.None &&
                    source2[i - 1].state == DiffType.Deleted &&
                    source1[i].state == DiffType.Deleted &&
                    source1[i - 1].state == DiffType.None)
                    break;
                if (source1[i].state == DiffType.None &&
                    source1[i - 1].state == DiffType.Deleted &&
                    source2[i].state == DiffType.Deleted &&
                    source2[i - 1].state == DiffType.None)
                    break;
                if (source1[i].state == DiffType.None && source2[i].state == DiffType.None)
                    break;
            }
            i--;
            int iFinish = i;

            if (isConflict)//были удаленные как справа так и слева
            {
                var version1 = source1.Expand(iStart, iFinish);
                var version2 = source2.Expand(iStart, iFinish);

                if (version1.Equals(version2))
                    result.AddRange(version1);
                else
                    result.Add(new ConflictedLine(version1, version2));
            }
            else
            {
                //были удаленные тролько с одной стороны, конфликта нет, просто принимаем новые строки
                var version = source1.Expand(iStart, iFinish);
                if (source2[iStart].state == DiffType.Deleted)
                    version = source2.Expand(iStart, iFinish);

                result.AddRange(version);
            }
        }

        private static void FlushSubLines(Lines subLines1, Lines subLines2, Lines result)
        {
            if (subLines1 != null && subLines2 != null)
            {
                if (subLines1.Equals(subLines2))
                    result.AddRange(subLines1);
                else
                    result.Add(new ConflictedLine(subLines1, subLines2));
                return;
            }

            if (subLines1 != null)
                result.AddRange(subLines1);
            if (subLines2 != null)
                result.AddRange(subLines2);
        }
    }
}
